package com.lfy.modules.monitor.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lfy.modules.common.anno.NoNeedLog;
import com.lfy.modules.common.domain.PageResultVO;
import com.lfy.modules.common.domain.R;
import com.lfy.modules.common.utils.PageUtil;
import com.lfy.modules.monitor.domain.ThreadPoolMonitor;
import com.lfy.modules.monitor.domain.vo.SystemCpuUsingEvent;
import com.lfy.modules.monitor.domain.vo.SystemDetailVO;
import com.lfy.modules.monitor.domain.vo.ThreadPoolDetailVO;
import com.lfy.modules.monitor.domain.vo.ThreadPoolMonitorVO;
import com.lfy.modules.monitor.mapper.ThreadPoolMonitorMapper;
import com.lfy.modules.monitor.service.MonitorService;
import com.lfy.modules.monitor.service.SseService;
import com.lfy.modules.monitor.task.SseTaskCache;
import com.lfy.modules.common.utils.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.software.os.OperatingSystem;

import javax.annotation.Resource;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author lfy
 * @since 2024-04-02
 */
@Service
@Slf4j
public class MonitorServiceImpl extends ServiceImpl<ThreadPoolMonitorMapper, ThreadPoolMonitor> implements MonitorService, SseService {
    @Resource
    private ThreadPoolMonitorMapper threadPoolMonitorMapper;
    @Resource(name = "mainThreadPool")
    private ThreadPoolExecutor mainThreadPool;


    private static final SystemInfo SI = new SystemInfo();

    private static final OperatingSystem OS = SI.getOperatingSystem();

    private static final CentralProcessor PROCESSOR = SI.getHardware().getProcessor();

    private static final GlobalMemory MEMORY = SI.getHardware().getMemory();

    private static final Integer B = 1024;

    private static final Integer KB = 1024 * 1024;

    private static final Integer MB = 1024 * 1024 * 1024;

    @Override
    public R<ThreadPoolDetailVO> getThreadPoolDetail() {
        ThreadPoolDetailVO threadPoolDetailVO = new ThreadPoolDetailVO();

        Integer taskByTimeOne = threadPoolMonitorMapper.getTaskByTime(TimeUtils.getTimeBeforeHours(1));
        Integer taskByTimeFive = threadPoolMonitorMapper.getTaskByTime(TimeUtils.getTimeBeforeHours(5));
        Integer queueItemSmallSizeByCreateTimeOne = threadPoolMonitorMapper.getQueueItemSmallSizeByCreateTime(TimeUtils.getTimeBeforeHours(1));
        Integer queueItemSmallSizeByCreateTimeFive = threadPoolMonitorMapper.getQueueItemSmallSizeByCreateTime(TimeUtils.getTimeBeforeHours(5));

        ThreadPoolDetailVO.ThreadLoad poolMaxLoadByCreateTimeOne = threadPoolMonitorMapper.getPoolMaxLoadByCreateTime(TimeUtils.getTimeBeforeHours(1));
        ThreadPoolDetailVO.ThreadLoad poolMaxLoadByCreateTimeFive = threadPoolMonitorMapper.getPoolMaxLoadByCreateTime(TimeUtils.getTimeBeforeHours(5));
        if(Objects.isNull(poolMaxLoadByCreateTimeFive)){
            poolMaxLoadByCreateTimeFive = new ThreadPoolDetailVO.ThreadLoad();
            poolMaxLoadByCreateTimeFive.setMaxLoad(0.0);
            poolMaxLoadByCreateTimeFive.setTime(new Date());
        }
        if(Objects.isNull(poolMaxLoadByCreateTimeOne)) {
            poolMaxLoadByCreateTimeOne = new ThreadPoolDetailVO.ThreadLoad();
            poolMaxLoadByCreateTimeOne.setMaxLoad(0.0);
            poolMaxLoadByCreateTimeOne.setTime(new Date());
        }
        //一个小时内最大负载
        threadPoolDetailVO.setMaxLoadInFiveHours(poolMaxLoadByCreateTimeFive);
        //五个小时内最大负载
        threadPoolDetailVO.setMaxLoadInOneHours(poolMaxLoadByCreateTimeOne);

        //当前任务总数
        long completedTaskCount = mainThreadPool.getCompletedTaskCount();
        //一个小时内处理了多少任务
        threadPoolDetailVO.setTaskSumInOneHours(completedTaskCount - ((taskByTimeOne!= null && taskByTimeOne >= 0) ? (completedTaskCount - taskByTimeOne) : 0));
        //五个小时内处理了多少任务
        threadPoolDetailVO.setTaskSumInFiveHours(completedTaskCount - ((taskByTimeFive!= null && taskByTimeFive >= 0) ? completedTaskCount - taskByTimeOne : 0));
        //五个小时内 最小队列剩余容量
        threadPoolDetailVO.setQueueSurPlusInFiveHours(queueItemSmallSizeByCreateTimeFive == null ? 0 : queueItemSmallSizeByCreateTimeFive);
        //一个小时内，最小队列剩余容量
        threadPoolDetailVO.setQueueSurPlusInOneHours(queueItemSmallSizeByCreateTimeOne == null ? 0 : queueItemSmallSizeByCreateTimeOne);

        return R.success(threadPoolDetailVO);
    }

    /**
     * 获取记录数据
     *
     * @param pageSize 每页大小
     * @param pageNum  当前页
     * @return r
     */
    @Override
    public R<PageResultVO<ThreadPoolMonitorVO>> getThreadList(Integer pageSize, Integer pageNum) {
        Page<ThreadPoolMonitor> threadPoolMonitorPage = new Page<>(pageNum, pageSize);

        IPage<ThreadPoolMonitor> page = this.page(threadPoolMonitorPage, new LambdaQueryWrapper<ThreadPoolMonitor>().orderByDesc(ThreadPoolMonitor::getId));
        List<ThreadPoolMonitor> records = page.getRecords();
        List<ThreadPoolMonitorVO> vos = records.stream().map(item -> {
            ThreadPoolMonitorVO threadPoolMonitorVO = new ThreadPoolMonitorVO();
            BeanUtils.copyProperties(item, threadPoolMonitorVO);
            threadPoolMonitorVO.setCreateTime(TimeUtils.toYearMonthDayAndTime(item.getCreateTime()));
            return threadPoolMonitorVO;
        }).collect(Collectors.toList());
        return R.success(PageUtil.buildPageResultVO(page, vos));
    }

    @Override
    public R<SystemDetailVO> getSystem() {
        SystemDetailVO systemDetailVO = new SystemDetailVO();
        CompletableFuture<Void> systemFuture = CompletableFuture.runAsync(() -> {
            systemDetailVO.setSystemName(OS.getFamily().toString().concat(" ").concat(OS.getVersion().toString()));
            systemDetailVO.setCpuName(PROCESSOR.getName());
            systemDetailVO.setCpuCoreNum(PROCESSOR.getLogicalProcessorCount());
            systemDetailVO.setAllMemory(MEMORY.getTotal() / 1024 / 1024 + " MB");
            systemDetailVO.setUsingMemory(MEMORY.getAvailable() / 1024 / 1024 + " MB");
        }, mainThreadPool);

        CompletableFuture<Void> jvmFuture = CompletableFuture.runAsync(() -> {
            StopWatch stopWatch = new StopWatch("JVM task");
            stopWatch.start();

            // 获取JVM信息
            List<MemoryPoolMXBean> memoryPoolBeans = ManagementFactory.getMemoryPoolMXBeans();
            for (MemoryPoolMXBean pool : memoryPoolBeans) {
                String name = pool.getName();
                // 新生代
                if (name.contains("Eden")) {
                    MemoryUsage usage = pool.getUsage();
                    long used = usage.getUsed();
                    long committed = usage.getCommitted();
                    long max = usage.getMax();
                    double usageRatio = (double) used / (double) max;
                    systemDetailVO.setEdenMax(formatBytes(max));
                    systemDetailVO.setEdenUsed(formatBytes(used));
                    systemDetailVO.setEdenRatio(String.format("%.2f%%", usageRatio * 100));
                    systemDetailVO.setEdenCommitted(formatBytes(committed));
                    // 老年代
                } else if (name.contains("Tenured") || name.contains("Old")) {
                    MemoryUsage usage = pool.getUsage();
                    long used = usage.getUsed();
                    long committed = usage.getCommitted();
                    long max = usage.getMax();
                    double usageRatio = (double) used / (double) max;
                    systemDetailVO.setOldMax(formatBytes(max));
                    systemDetailVO.setOldUsed(formatBytes(used));
                    systemDetailVO.setOldRatio(String.format("%.2f%%", usageRatio * 100));
                    systemDetailVO.setOldCommitted(formatBytes(committed));
                }
            }
            // 输出GC统计信息 用于了解新生代和老年代的回收情况
            List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
            List<SystemDetailVO.Management> managementList = gcBeans.stream().map(gcBean -> {
                SystemDetailVO.Management management = new SystemDetailVO.Management();
                management.setGarbageName(gcBean.getName());
                management.setCount(gcBean.getCollectionCount());
                management.setTime(gcBean.getCollectionTime());
                return management;
            }).collect(Collectors.toList());
            systemDetailVO.setManagementList(managementList);
            stopWatch.stop();
            String s = stopWatch.prettyPrint();
            log.info(s);

        }, mainThreadPool);

        try {
            CompletableFuture.allOf(systemFuture, jvmFuture).get();
        } catch (Exception e) {
            log.error("in package com.lfy.modules.monitor.service.impl.MonitorServiceImpl;function:getSystem() error", e);
        }

        return R.success(systemDetailVO);
    }


    /**
     * 将字节数转换为便于阅读的KB, MB, GB等格式
     */
    private static String formatBytes(long bytes) {
        if (bytes < B) {
            return bytes + " B";
        } else if (bytes < KB) {
            return String.format("%.2f KB", bytes / 1024.0);
        } else if (bytes < MB) {
            return String.format("%.2f MB", bytes / (1024.0 * 1024));
        } else {
            return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
        }
    }

    @Override
    @NoNeedLog
    public void sendSse() {
        SystemCpuUsingEvent systemCpuUsingEvent = new SystemCpuUsingEvent();
        systemCpuUsingEvent.setCpuUsing(builderCpuUsing2());
        systemCpuUsingEvent.setMemoryUsing(builderMemoryUsing());
        SseTaskCache.sendMsg(systemCpuUsingEvent);
    }

    private double builderCpuUsing() {
        // 获取前一次CPU负载ticks数据
        long[] prevTicks = PROCESSOR.getSystemCpuLoadTicks();
// 获取当前CPU负载ticks数据
        long[] ticks = PROCESSOR.getSystemCpuLoadTicks();
// 计算Nice（低优先级）CPU时间片的变化量（delta）
        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
// 计算Interrupt Request (IRQ) CPU时间片的变化量（delta）
        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
// 计算Soft Interrupt Request (SoftIRQ) CPU时间片的变化量（delta）
        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
// 计算Steal（虚拟机环境下被其他虚拟机占用）CPU时间片的变化量（delta）
        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
// 计算System（内核态）CPU时间片的变化量（delta）
        long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
// 计算User（用户态）CPU时间片的变化量（delta）
        long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
// 计算I/O Wait（等待输入输出操作完成）CPU时间片的变化量（delta）
        long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
// 计算Idle（空闲）CPU时间片的变化量（delta）
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
// 计算所有类型CPU时间片变化量之和，即总CPU时间片（delta）
        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
        double v = (1.0 - (idle * 1.0 / totalCpu)) * 100;
//        log.info("CPU利用率 ={}", new DecimalFormat("#.##%").format(1.0 - (idle * 1.0 / totalCpu)));
        // 使用 BigDecimal 控制精度并保留两位小数
//        BigDecimal cpuUsagePercentage = new BigDecimal(v).setScale(2, RoundingMode.HALF_UP);

        return v;

    }

    private double builderCpuUsing2() {
        return PROCESSOR.getSystemCpuLoad() * 100;
    }

    private double builderMemoryUsing() {
        // 获取内存总量、已用内存和可用内存
        long totalMemory = MEMORY.getTotal();
        long usedMemory = MEMORY.getAvailable();
        double memoryUsagePercentage = ((double) usedMemory / totalMemory) * 100;
        // 使用DecimalFormat保留两位小数
        DecimalFormat df = new DecimalFormat("#.00");
        String formattedValue = df.format(memoryUsagePercentage);
        // 将格式化后的字符串转换回double类型（实际应用中通常不需要这一步，直接返回字符串即可）
        return Double.parseDouble(formattedValue);
    }
}
